package com.eclink.iot.rfidclient.manager;

import com.eclink.iot.rfidclient.mqtt.MQTTUtils;
import com.eclink.iot.rfidclient.websocket.SessionHolder;
import com.eclink.iot.rfidclient.websocket.WebServerEndpoint;
import com.fazecast.jSerialComm.SerialPort;
import com.fazecast.jSerialComm.SerialPortDataListener;
import com.fazecast.jSerialComm.SerialPortEvent;
import jakarta.websocket.Session;
import lombok.Data;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.*;

@Slf4j
public class RFIDManager {

    // 存储可用的串口列表
    private static SerialPort[] serialPorts = null;

    // RFID当前使用的串口
    @Getter
    private static SerialPort currentPort = null;

    // 读取到的串口数据
    public static String serialData;

    public static EPCList operList;

    // 超时标识
    public static int ackTimoutFlag;

    private static final int baudRateDefault = 115200;

    public static void init() {
        SerialPort[] serialPorts = getSerialPorts();
        for (SerialPort serialPort : serialPorts) {
            if (serialPort.getVendorID()>0) {
                currentPort = serialPort;
                openSerialPort(serialPort);
                break;
            }
        }
    }

    //获取可使用的串口号
    private static SerialPort[] getSerialPorts(){
        if (serialPorts == null){
            serialPorts = SerialPort.getCommPorts();
        }
        return serialPorts;
    }

    //串口接收数据
    public static byte [] serialRecvData(){
        byte[] readData = null;

        if(!currentPort.isOpen()){
            System.out.println("串口异常");
            return null;
        }

        List<Byte> bufferList = new ArrayList<>();
        int index = 0;
        byte[] readByte = new byte[2];
        int len = 0;
        int noReadCount = 0;

        if(currentPort.bytesAvailable()>0){

            System.out.println("byteslength=" + currentPort.bytesAvailable());

            while (true) {
                int readState = currentPort.readBytes(readByte, 1);
                if(readState != 1){
                    noReadCount++;
                    if(noReadCount > 500){
                        System.out.println("接收超时");
                        return null;
                    }
                    continue;
                }
                index++;
                if(index == 1){         //接收到包头
                    if(readByte[0] == Commands.head){
                        bufferList.add(readByte[0]);
                    }else{
                        index = 0;
                    }
                }else if(index == 2){   //接收包的长度
                    bufferList.add(readByte[0]);
                    len = (readByte[0] & 0xff);
                    System.out.println("len=" + len);
                }else{
                    bufferList.add(readByte[0]);
                    if(index >= (len+2)){
                        byte[] packetBuf = new byte[bufferList.size()];
                        int packetIndex = 0;
                        for (Object object : bufferList) {
                            packetBuf[packetIndex++] = (byte)object;
                        }
                        readData = packetBuf;
                        break;
                    }
                }
            }
        }



        return readData;
    }

    public static void openSerialPort(SerialPort serialPort){

        serialPort.setBaudRate(baudRateDefault);
        serialPort.setComPortTimeouts(SerialPort.TIMEOUT_READ_BLOCKING|SerialPort.TIMEOUT_WRITE_BLOCKING, 100, 100);
        serialPort.setRTS();
        serialPort.setFlowControl(SerialPort.FLOW_CONTROL_DISABLED);
        serialPort.setComPortParameters(baudRateDefault, 8, SerialPort.ONE_STOP_BIT, SerialPort.NO_PARITY);
        serialPort.openPort();
        currentPort = serialPort;
        // 移除掉旧的监听事件
        currentPort.removeDataListener();
        currentPort.addDataListener(new SerialPortDataListener(){
            @Override
            public int getListeningEvents() {
                return SerialPort.LISTENING_EVENT_DATA_AVAILABLE;
            }

            @Override
            public void serialEvent(SerialPortEvent event){
                if(event.getEventType() != SerialPort.LISTENING_EVENT_DATA_AVAILABLE){
                    return;
                }
                while (currentPort.bytesAvailable()!=0) {
                    System.out.println("监听端口的事件");
                    byte[] data;
                    data  = serialRecvData();

                    //解析数据
                    if(data != null)
                    {
                        try {
                            rfidClientReceived(data);
                        } catch (IOException e) {
                            log.error(e.getMessage());
                        }
                        System.out.println("finish");
                    }
                }
            }
        });

        checkPortOpen();

    }

    /**
     * 检查端口是否有效
     */
    public static void checkPortOpen(){
        CommandContent commandContent = new CommandContent();
        commandContent.pAddress = (byte) 0;
        commandContent.pCommand = Commands.cmd_reset;
        commandContent.pData = new byte[0];
        sendData(commandContent.GetCommandBytes());
    }

    //读
    public static byte MsgRead(byte address, byte memBank, int wordAddr, short wordCnt, int password){
        byte index = 0;
        CommandContent commandContent = new CommandContent();
        byte[] data = new byte[11];

        data[index++] = memBank;
        data[index++] = (byte)(wordAddr/256/256/256);
        data[index++] = (byte)(wordAddr/256/256%256);
        data[index++] = (byte)(wordAddr/256%256);
        data[index++] = (byte)(wordAddr%256);
        data[index++] = (byte)(wordCnt/256);
        data[index++] = (byte)(wordCnt%256);
        data[index++] = (byte)(password/256/256/256);
        data[index++] = (byte)(password/256/256%256);
        data[index++] = (byte)(password/256%256);
        data[index++] = (byte)(password%256);

        commandContent.pAddress = address;
        commandContent.pCommand = Commands.cmd_read;
        commandContent.pData = data;

        return sendData(commandContent.GetCommandBytes());
    }

    public static byte sendData(Object [] data){

        int index = 0;
        byte[] sendBuf = new byte[data.length];

        for (Object object : data) {
            sendBuf[index++] = (byte)object;
        }

        serialSendData(sendBuf);

        return 1;
    }

    //串口发送数据
    public static void serialSendData(byte [] buff){
        if(currentPort.isOpen())
        {
            currentPort.writeBytes(buff, buff.length);
        }
        else{
            System.err.println("串口未打开");
        }
    }

    //获取系统当前时间
    private static String getSystemCurrentTime(){
        Date currentTime = new Date();
        SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.SSS");
        return sdf.format(currentTime);
    }

    /********************************私有接口*********************************/
    public static String byteArrayToHex(byte [] bytes){
        Formatter formatter = new Formatter();
        for (byte b : bytes) {
            formatter.format("%02X ", b);
        }

        return formatter.toString();
    }

    //接收数据
    private static void rfidClientReceived(byte[] data) throws IOException {

        // hex显示接收的数据包
        System.out.print("[" + getSystemCurrentTime() + "]接收数据：");
        System.out.println(byteArrayToHex(data));

        //此处应该使用委托
        //解析数据
        rfidUnpack(data);
    }

    public static void rfidReceivePacket(byte [] packet){
//        System.out.println("[" + getSystemCurrentTime() + "]Receive packet: " + byteArrayToHex(packet));
        System.out.println("收到串口的信息："+byteArrayToHex(packet));
        // 超时标识
        ackTimoutFlag = 0;
    }

    //解析读取标签的内容
    private static void rfid6CRead(byte [] data) throws IOException {
        if(data.length <= 2){
            switch (data[0]) {
                case 0x10:
                    System.out.println("操作成功");
                    break;
                case 0x11:System.out.println("操作失败");
                    break;
                case 0x32:System.out.println("读标签错误");
                    break;
                case 0x36:System.out.println("无可操作标签");
                    break;
                case 0x37:System.out.println("成功盘存但访问失败");
                    break;
                case 0x40:System.out.println("访问标签错误或密码错误");
                    break;
                case 0x41:System.out.println("无效的参数");
                    break;
                case 0x43:System.out.println("操作长度越界");
                    break;
                default:
                    System.out.println("其他");
            }
            System.out.println("标签的标志位："+data[0]);
            String res = String.valueOf(data[0]);
            SessionHolder.sendMessage(res);

//            MQTTUtils.sendMessage("read-tag", res);
            return;
        }

        EPCList tagData = new EPCList();
        int epcLen = 0;
        int index = 0;
        short tagCount = 0;
        int dataLen = 0;
        tagCount = (short)((data[index++]&0xff) << 8);
        tagCount |= (short)(data[index++]&0xff);

        dataLen = data[index++]&0xff;

        tagData.pc[0] = data[index++];
        tagData.pc[1] = data[index++];
        System.out.println("pc= " + byteArrayToHex(tagData.pc));

        epcLen = ((tagData.pc[0]&0xff)/8)*2;
        System.out.println("epcLen=" + epcLen);
        if(epcLen > (data.length - 3)){
            System.out.println("epcLen error");
            return;
        }

        byte [] epc = new byte[epcLen];

        for (int i = 0; i < epcLen; i++) {
            epc[i] = data[index++];
        }
        tagData.epc = byteArrayToHex(epc);
        tagData.crc[0] = data[index++];
        tagData.crc[1] = data[index++];
        System.out.println("crc= " + byteArrayToHex(tagData.crc));

        int dataepcLen = dataLen-2-epcLen-2;
        System.out.println("dataepcLen=" + dataepcLen);

        byte [] dataepc = new byte[dataepcLen];
        for (int i = 0; i < dataepcLen; i++) {
            dataepc[i] = data[index++];
        }

        tagData.data = byteArrayToHex(dataepc);

        short readLen = 0;
        readLen = (short)((data[index++]&0xff) << 8);
        readLen |= (short)(data[index++]&0xff);

        tagData.ant = (byte)(data[index++]&0x03);

        operList = tagData;

        SessionHolder.sendMessage(tagData.epc);


//        MQTTUtils.sendMessage("read-tag", tagData.epc);

    }

    private static void rfidAnalyze(byte cmd, byte [] data) throws IOException {
        switch (cmd) {

            case Commands.cmd_read: //读标签
                rfid6CRead(data);
                break;
            case Commands.cmd_reset:
//                MQTTUtils.sendMessage("rfid-connect-ready", "ok");
                break;
            default:
                System.out.println("其他指令");
                break;
        }
    }

    private static void rfidUnpack(byte [] buffer) throws IOException {

        CommandContent commandContent = new CommandContent();
        int bufferLen = buffer.length;
        int packetLen = 0;
        int packetIndex = 0;
        int index = 0;
        int switchIndex = 0;
        int packetStartIndex = 0;

        while(index < bufferLen)
        {
            byte bufChar = buffer[index++];
            switch (switchIndex) {
                case 0:
                    //head
                    if(bufChar == Commands.head)
                    {
                        switchIndex++;
                        packetStartIndex = index-1;
                    }
                    break;
                case 1:
                    //len
                    commandContent.pLength= bufChar;
                    packetLen = commandContent.pLength - 3;
                    switchIndex++;
                    break;
                case 2:
                    //address
                    commandContent.pAddress= bufChar;
                    switchIndex++;
                    break;
                case 3:
                    //cmd
                    commandContent.pCommand= bufChar;
                    commandContent.pData = null;
                    switchIndex++;
                    if (packetLen == 0) {
                        switchIndex++;//没有data段
                    }
                    else if(packetLen > 0){
                        //先清空，再给内存
                        commandContent.pData = new byte[packetLen];

                    }else{
                        switchIndex = 0;
                    }
                    break;
                case 4:
                    //data
                    commandContent.pData[packetIndex++] = bufChar;
                    if(packetIndex >= packetLen){
                        switchIndex++;
                        packetIndex = 0;
                    }
                    break;
                case 5:
                    //crc
                    commandContent.pCheckSum= bufChar;

                    //使用委托接口将未解析的整包数据发送给用户
                    rfidReceivePacket(Arrays.copyOfRange(buffer, packetStartIndex, index));


                    //这里需先进行crc校验，校验通过后将数据进行下一步分析
                    rfidAnalyze(commandContent.pCommand, commandContent.pData);
                    switchIndex=0;
                    break;
                default:
                    switchIndex = 0;
                    System.out.println("协议解析出错");
            }
        }

        serialData = byteArrayToHex(commandContent.pData);


    }

    public static void main(String[] args) {
        SerialPort[] serialPorts = getSerialPorts();
        for (SerialPort serialPort : serialPorts) {
            System.out.println(serialPort.getSystemPortPath());
            System.out.println(serialPort.getFlowControlSettings());
            System.out.println(serialPort.getVendorID());
            System.out.println(serialPort.getSerialNumber());
            System.out.println(serialPort.getPortLocation());
            System.out.println(serialPort.isOpen());
            System.out.println(serialPort.bytesAvailable());
            System.out.println(serialPort.getDescriptivePortName());
            System.out.println(serialPort.getSystemPortName());
            System.out.println(serialPort.getPortDescription());

        }
    }

}
